﻿using System;

namespace DimensionsHelper.Common.Metadata.Script;

/// <summary>
///     一个简单的元数据脚本解析器。
/// </summary>
public class Parser
{
    private readonly Lexer _lexer = new();

    /// <summary>
    ///     解析指定脚本字符串并返回AST。
    /// </summary>
    /// <param name="scriptText">脚本文本字符串</param>
    /// <returns>解析后的AST</returns>
    public Expression Parse(string scriptText)
    {
        _lexer.SetText(scriptText);
        _lexer.Lex();
        return ParseExpressionOperand();
    }


    private bool Optional(TokenKinds token)
    {
        if (_lexer.Token != token)
        {
            return false;
        }

        _lexer.Lex();
        return true;
    }


    private void Expect(TokenKinds token, bool goAhead = true)
    {
        if (_lexer.Token != token)
        {
            throw new Exception($"期望外的字符：{_lexer.TokenText}，应为：{token}，位置：{_lexer.TokenStart}");
        }

        if (goAhead)
        {
            _lexer.Lex();
        }
    }


    private T Expect<T>(TokenKinds token, Func<T> callback) where T : Expression
    {
        if (token == _lexer.Token)
        {
            return callback();
        }

        throw new Exception($"期望外的字符：{_lexer.TokenText}，应为：{token}，位置：{_lexer.TokenStart}。");
    }


    private Expression ParseExpressionOperand(Expression? operand = null)
    {
        Expression? leftOperand;
        switch (_lexer.Token)
        {
            case TokenKinds.Integer:
                leftOperand = ParseInteger();
                break;

            case TokenKinds.Double:
                leftOperand = ParseDouble();
                break;

            case TokenKinds.String:
                leftOperand = ParseString();
                break;

            case TokenKinds.Null:
                leftOperand = ParseNull();
                break;

            case TokenKinds.True:
            case TokenKinds.False:
                leftOperand = ParseBoolean();
                break;

            case TokenKinds.LeftParen:
                if (operand is { ExpressionKind: ExpressionKinds.Identifier or ExpressionKinds.Member })
                {
                    leftOperand = ParseCallExpression(operand);
                    break;
                }

                // 记录起始位置，用于修正表达式Text
                int parenStart = _lexer.TokenStart;
                _lexer.Lex();
                leftOperand = ParseExpressionOperand(operand);
                Expect(TokenKinds.RightParen);
                // 修正Text
                leftOperand.Finish(parenStart, _lexer.LastTokenEnd,
                    _lexer.GetText(parenStart, _lexer.LastTokenEnd));
                leftOperand.WithParentheses = true;
                break;

            case TokenKinds.LeftCurly:
                leftOperand = ParseCategorical();
                break;

            case TokenKinds.Identifier:
                leftOperand = ParseIdentifier();
                if (_lexer.Token is TokenKinds.Dot or TokenKinds.LeftBracket or TokenKinds.LeftParen)
                {
                    leftOperand = ParseExpressionOperand(leftOperand);
                }

                break;

            case TokenKinds.Dot:
                leftOperand = ParseMemberExpression(operand);
                if (_lexer.Token is TokenKinds.Dot or TokenKinds.LeftBracket or TokenKinds.LeftParen)
                {
                    leftOperand = ParseExpressionOperand(leftOperand);
                }
                break;

            case TokenKinds.LeftBracket:
                leftOperand = ParseIndexExpression(operand);
                if (_lexer.Token == TokenKinds.Dot)
                {
                    leftOperand = ParseExpressionOperand(leftOperand);
                }
                break;

            case TokenKinds.Subtract:
            case TokenKinds.Not:
                leftOperand = ParseUnaryExpression();
                break;

            default:
                throw new Exception($"无效的左值表达式：{_lexer.TokenText}，位置：{_lexer.TokenStart}。");
        }

        return BinaryExpression.IsBinaryOperator(_lexer.Token) ? ParseBinaryExpression(leftOperand) : leftOperand;
    }


    private UnaryExpression ParseUnaryExpression()
    {
        var expr = new UnaryExpression(_lexer.TokenStart);
        Expect(TokenKinds.Not);
        expr.Expression = ParseExpressionOperand();
        expr.Finish(_lexer.LastTokenEnd, _lexer.GetText(expr.Start, _lexer.LastTokenEnd));
        return expr;
    }


    private BinaryExpression ParseBinaryExpression(Expression leftOperand)
    {
        var expr = new BinaryExpression(_lexer.Token, leftOperand.Start)
        {
            LeftOperand = leftOperand
        };

        _lexer.Lex();
        Expression right = ParseExpressionOperand();

        //  检查两个二元操作符的优先级
        //  例如： A + B + C = D
        //  正确的结果应当是：其中，1.优先级： + -> 3， = -> 2，2.解析顺序总是从左往右的。
        //                  =             解析二元操作符时，如果右值是二元表达式
        //                 / \            1. 带有括号的表达式不需要比较。
        //                +   D           2. 如果右值表达式操作符优先级小于等于当前操作符优先级时：
        //               / \                 1). 将当前表达式设置成右值表达式的最左叶节点的左值
        //              +   C                2). 将右值表达式的左值设置为当前表达式的右值
        //             / \                3. 如果当前表达式操作符优先级大于右值表达式操作符优先级时，
        //            A   B                  直接将右值表达式设置成当前表达式的右值。

        if (right.WithParentheses || right.ExpressionKind != ExpressionKinds.Binary)
        {
            expr.RightOperand = right;
        }
        else
        {
            BinaryExpression rightBinary = (BinaryExpression)right;
            int curPriority = BinaryExpression.GetBinaryOperatorPriority(expr.Operator);
            int rightPriority = BinaryExpression.GetBinaryOperatorPriority(rightBinary.Operator);

            if (curPriority < rightPriority)
            {
                expr.RightOperand = right;
            }
            else
            {
                BinaryExpression leftLeaf = rightBinary;
                while (leftLeaf.ExpressionKind == ExpressionKinds.Binary)
                {
                    if (leftLeaf.LeftOperand?.ExpressionKind == ExpressionKinds.Binary)
                    {
                        leftLeaf = (BinaryExpression)leftLeaf.LeftOperand;
                    }
                    else
                    {
                        break;
                    }
                }

                expr.RightOperand = leftLeaf.LeftOperand;
                leftLeaf.LeftOperand = expr;
                // 更新左节点和右节点范围
                rightBinary.Finish(expr.Start, right.End, _lexer.GetText(expr.Start, right.End));
                expr.Finish(_lexer.LastTokenEnd,
                    _lexer.GetText(expr.Start, expr.RightOperand?.End ?? _lexer.LastTokenEnd));
                return rightBinary;
            }
        }

        expr.Finish(_lexer.LastTokenEnd, _lexer.GetText(expr.Start, _lexer.LastTokenEnd));
        return expr;
    }


    private IntegerExpression ParseInteger()
    {
        var expr = new IntegerExpression(_lexer.TokenStart);

        int val;

        if (_lexer.IsOctalNumber)
        {
            val = Convert.ToInt32(_lexer.TokenText, 8);
        }
        else if (_lexer.IsHexNumber)
        {
            val = Convert.ToInt32(_lexer.TokenText, 16);
        }
        else
        {
            val = Convert.ToInt32(_lexer.TokenText);
        }

        expr.Finish(_lexer.Position, val.ToString());
        _lexer.Lex();
        return expr;
    }


    private DoubleExpression ParseDouble()
    {
        var expr = new DoubleExpression(_lexer.TokenStart);
        expr.Finish(_lexer.Position, _lexer.TokenText);
        _lexer.Lex();
        return expr;
    }


    private BooleanExpression ParseBoolean()
    {
        var expr = new BooleanExpression(_lexer.TokenStart);
        expr.Finish(_lexer.Position, _lexer.TokenText);
        _lexer.Lex();
        return expr;
    }


    private StringExpression ParseString()
    {
        var expr = new StringExpression(_lexer.TokenStart);
        expr.Finish(_lexer.Position, _lexer.TokenText.Substring(1, _lexer.TokenText.Length - 2));
        _lexer.Lex();
        return expr;
    }


    private NullExpression ParseNull()
    {
        var nullExpr = new NullExpression(_lexer.TokenStart);
        nullExpr.Finish(_lexer.Position, _lexer.TokenText);
        _lexer.Lex();
        return nullExpr;
    }


    private CategoryExpression ParseCategoricalElementExpression()
    {
        var catElementExpr = new CategoryExpression(_lexer.TokenStart)
        {
            Caret = Optional(TokenKinds.Caret)
        };

        if (catElementExpr.Caret || _lexer.Token == TokenKinds.Identifier)
        {
            catElementExpr.LowerBoundary = Expect(TokenKinds.Identifier, ParseIdentifier);
        }
        else
        {
            catElementExpr.Value = Expect(TokenKinds.Integer, ParseInteger);
        }

        // 区间表达式只能使用 Identifier
        if (_lexer.Token == TokenKinds.Dot)
        {
            if (catElementExpr.Value != null)
            {
                throw new Exception($"无效的分类元素：{_lexer.TokenText}，位置：{_lexer.TokenStart}。");
            }

            _lexer.Lex();
            Expect(TokenKinds.Dot);

            // 不是 ','或'}'，解析上限。
            if (_lexer.Token != TokenKinds.Comma && _lexer.Token != TokenKinds.RightCurly)
            {
                catElementExpr.UpperBoundary = Expect(TokenKinds.Identifier, ParseIdentifier);
            }
        }
        // 对于单值区间，将上下限设置成相同的值。
        else if (catElementExpr.LowerBoundary != null)
        {
            catElementExpr.UpperBoundary = catElementExpr.LowerBoundary;
        }

        catElementExpr.Finish(_lexer.LastTokenEnd, _lexer.GetText(catElementExpr.Start, _lexer.LastTokenEnd));
        return catElementExpr;
    }


    private CategoricalExpression ParseCategorical()
    {
        var expr = new CategoricalExpression(_lexer.TokenStart);
        Expect(TokenKinds.LeftCurly);

        while (_lexer.Token != TokenKinds.RightCurly && _lexer.Token != TokenKinds.Eof)
        {
            expr.Add(ParseCategoricalElementExpression());
            if (_lexer.Token != TokenKinds.RightCurly)
            {
                Expect(TokenKinds.Comma);
            }
        }

        Expect(TokenKinds.RightCurly);
        expr.Finish(_lexer.LastTokenEnd, _lexer.GetText(expr.Start, _lexer.LastTokenEnd));
        return expr;
    }


    private Identifier ParseIdentifier()
    {
        var id = new Identifier(_lexer.TokenStart);
        id.Finish(_lexer.Position, _lexer.TokenText);
        _lexer.Lex();
        return id;
    }


    private IndexExpression ParseIndexExpression(Expression? obj)
    {
        if (obj == null)
        {
            throw ThrowHelper.InvalidToken(_lexer.Position, _lexer.TokenText);
        }
        
        var expr = new IndexExpression(obj.Start)
        {
            Object = obj
        };

        // 跳过 [
        Expect(TokenKinds.LeftBracket);
        expr.Index = ParseExpressionOperand();
        Expect(TokenKinds.RightBracket);
        expr.Finish(_lexer.Position, _lexer.GetText(obj.Start));

        return expr;
    }


    private MemberExpression ParseMemberExpression(Expression? obj)
    {
        if (obj == null)
        {
            throw ThrowHelper.InvalidToken(_lexer.TokenStart, _lexer.TokenText);
        }
        
        var expr = new MemberExpression(obj.Start)
        {
            Object = obj
        };

        // 跳过 .
        Expect(TokenKinds.Dot);
        expr.Member = ParseIdentifier();
        expr.Finish(_lexer.Position, _lexer.GetText(obj.Start));

        return expr;
    }


    private CallExpression ParseCallExpression(Expression callee)
    {
        var expr = new CallExpression(callee.Start)
        {
            Callee = callee
        };

        // 跳过 '('
        Expect(TokenKinds.LeftParen);

        while (_lexer.Token != TokenKinds.RightParen && _lexer.Token != TokenKinds.Eof)
        {
            expr.PushArgument(ParseExpressionOperand());

            if (_lexer.Token != TokenKinds.RightParen)
            {
                Expect(TokenKinds.Comma);
            }
        }

        Expect(TokenKinds.RightParen);
        expr.Finish(_lexer.LastTokenEnd, _lexer.GetText(callee.Start, _lexer.LastTokenEnd));

        return expr;
    }
}